"""
cdn.client_api

This file together with admin_api define the API for using cdn programmatically. 
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, 'settings from django.conf', 2)
from django.conf import settings

debug.importing(__name__, 'core, chord_api from jackson.cdn')
from jackson.cdn import core, chord_api

debug.importing(__name__, 'ContentEntry from jackson.cdn.models')
from jackson.cdn.models import ContentEntry

debug.importing(__name__, 'i_am_responsible_for from jackson.cdn.core')
from jackson.cdn.core import i_am_responsible_for

debug.importing(__name__, 'net from jackson.cdn')
from jackson.cdn import net

debug.importing(__name__, 'ext_json as json')
from jackson.extjson import ext_json as json

debug.post_import(__name__)

### End of imports

def get_url(the_key):
    """ Returns the URL from which the client can download the content associated to the requested key.
    @author: ep
    @param key: key to get URL for
    @rtype: string
    @return: a URL where content for key can be downloaded, or "DNE" if this server can't find that content.
    """
    context = [__name__, 'get_url()']
    debug.enter(context, { 'key' : the_key })
    try:
        # Check if we have an entry for the_key
        try:
            debug.comment(context, 'Looking for an entry with the specified key')
            entry = ContentEntry.objects.get(key=the_key)
            debug.comment(context, 'Found one!')
        except:
            debug.comment(context, 'There is no such entry')
            # If we don't have an entry for the key,
            # check if we're responsible for it
            debug.comment(context, 'Checking if I am responsible for this content')
            if core.i_am_responsible_for(the_key):
                debug.comment(context, 'I am responsible for it')
                debug.comment(context, 'We must have an invalid key')
                result = "DNE"
            else:        
                debug.comment(context, 'I am not responsible for it')
                # If I'm not responsible for the key, find out who is and ask them
                debug.comment(context, 'Looking up the server who is responsible for it')
                the_server = chord_api.lookup(the_key)
                debug.comment(context, 'Proxying to that server')
                result = net.get_url(the_server, the_key)
            if len(result) == 0:
                debug.exception(context,"cdn.client_api.get_url() problem with lookup result, i didn't yet have the key.")
            else:
                debug.comment(context,"result length is greater than 0:" + str(len(result)))
            debug.leave(context, { 'url' : result })
            return result
        
        # Check if we have the content on this server
        debug.comment(context, 'Checking if I am hosting this content')
        if entry.status in [ 'H', 'O' ]:
            debug.comment(context, 'I am hosting the content, its status is: ' + entry.status)
            url = entry.url
            if len(url) == 0:
                debug.exception(context,"problem with lookup result, i'm hosting the content! setting to DNE")
                url = 'DNE'
            debug.leave(context, { 'url' : url })
            # If we do, return the url where the file is served.
            #return entry.file.url
            return url
        else:
            debug.comment(context, 'I am not hosting the content')
            
            # Check if I am responsible for this file
            debug.comment(context, 'Checking if I am responsible for this content')
            if i_am_responsible_for(the_key):
                debug.comment(context, 'I am responsible for it')
                
                debug.comment(context, 'Incrementing its download urgency')
                # If I am, note the get_url miss
                entry.urgency = entry.urgency + settings.GET_URL_MISS_INCREMENT
                
                # and query the server I think has it.
                debug.comment(context, 'Proxying to the server who I think has it')
                result = net.get_url(entry.server, the_key)
                if len(result) == 0:
                    debug.exception(context,"cdn.client_api.get_url() problem with lookup result, i don't have the content")
            else:
                # I should never have an entry for which I have neither content nor responsibility
                debug.exception(context, 'Extraneous ContentEntry found. Cleanup is failing.')
                result = "DNE"
    except Exception, e:
        debug.exception(context, str(e))
        result = 'DNE'
    
    debug.leave(context, { 'url' : result })
    return result

def register_key(new_key, new_url, md5_checksum='', new_filename='', new_mimetype='text/plain'):
    """ Adds a new key/File pair to the network
    @author: ep
    @param new_key: key to add
    @param new_file: File object to add
    @rtype: None
    """
    context = [__name__, 'register_key()']
    debug.enter(context, { 'key' : new_key, 'url' : new_url, 'filename' : new_filename, 'mimetype' : new_mimetype})
    try:
        # Get the md5 checksum if it wasn't given
        #if md5_checksum == None:
        #    md5_checksum = core.md5_checksum(new_file)
        
        debug.comment(context, 'Checking if I am responsible for this content')
        me = chord_api.get_me()
        # Check if I am responsible for this content
        if i_am_responsible_for(new_key):
            debug.comment(context, 'I am responsible for it')
            # If I am, add it to my ContentEntry table as Here
            debug.comment(context, 'Adding it to my ContentEntry table as H')
            entry = ContentEntry(key=new_key)
            debug.comment(context, 'url is <' + new_url + '>')
            entry.url = new_url
            entry.filename = new_filename
            entry.mimetype = new_mimetype
            entry.status = 'H'
            entry.server = me
            entry.md5_checksum = md5_checksum
            entry.save()
        else:
            debug.comment(context, 'I am not responsible for it')
            
            debug.comment(context, 'Looking up who is')
            # If I am not, find out who is
            succ_of_new_key = chord_api.lookup(new_key)
            
            debug.comment(context, 'Adding to my ContentEntry table as O')
            # Add it to my ContentEntry table as Outgoing
            entry = ContentEntry(key=new_key)
            #entry.file = new_file
            debug.comment(context, 'Setting url', 2)
            entry.url = new_url
            debug.comment(context, 'Setting filename', 2)
            entry.filename = new_filename
            debug.comment(context, 'Setting mimetype', 2)
            entry.mimetype = new_mimetype
            debug.comment(context, 'Setting status', 2)
            entry.status = 'O'
            debug.comment(context, 'Setting server', 2)
            # @change: 03-16-2009
            # before, the next line was "server", which i assume is incorrect,
            entry.server = succ_of_new_key
            debug.comment(context, 'Setting md5_checksum', 2)
            entry.md5_checksum = md5_checksum
            debug.comment(context, 'Saving', 2)
            entry.save()
            
            # Delegate responsibility for this key to the proper server
            #print "jackson.cdn.client_api: \t\tcalling get_me()"
            #print "get_me in client_api"
            
            debug.comment(context, 'Informing custodian of its responsibility')
            debug.comment(context, 'passing the url ' + new_url)
            net.delegate_keys(succ_of_new_key, { new_key : { "server" : me, 
                                                            "md5_checksum" : md5_checksum, 
                                                            "filename": new_filename, 
                                                            "mimetype": new_mimetype,
                                                            "url" : new_url } })
        result = { 'success?' : True }
    except Exception, e:
        debug.exception(context, str(e))
        result = { 'exception' : str(e) }
    
    debug.leave(context, result)
    return result


def get_IP_keys():
    """
    Returns a list of keys of all entries in the content table whose mimetype is
    IP.
    @rtype: a list of keys
    """
    try:
        return ContentEntry.objects.get(mimetype="IP").values_list('key', flat=true)
    except:
        return []


def get_key_status(key):
    """
    For a given key, returns the single character status if the key is in the
    content table. Otherwise, returns False.
    @param key: the desired key
    @rtype: a string if the key exists, otherwise, False
    """
    try:
        return ContentEntry.objects.get(key=key).status
    except:
        return False

